<?php

namespace common\components;

use backend\modules\admin\models\Menu;
use Yii;
use yii\db\Query;
use yii\helpers\Html;
use yii\helpers\ArrayHelper;
use yii\helpers\StringHelper;
use common\assets\AppAsset;
use backend\modules\admin\components\Configs;
use backend\modules\admin\components\Helper as AdminHelper;

/**
 * Description of Helper
 *
 * @author fireloong
 */
class Helper
{
    protected static $entries = [];

    /**
     * 获取菜单数组
     * @param string $menuType 菜单类型
     * @return mixed
     */
    public static function getMenus($menuType = '')
    {
        $cacheKey = empty($menuType) ? 'menutype-all' : 'menutype-' . $menuType;

        $menus = Yii::$app->cache->getOrSet($cacheKey, function () use ($menuType) {
            $query = new Query();
            $query->select('*')->from('{{%menu}}')
                ->where(['published' => 1]);

            if (!empty($menuType) && is_string($menuType)) {
                $query->andWhere(['menutype' => $menuType]);
            }


            return $query->orderBy('ordering')->all();
        });

        $checkMenus = [];
        foreach ($menus as $menu) {
            if (empty($menu['route']) || AdminHelper::checkRoute($menu['route'])) {
                $checkMenus[] = $menu;
            } elseif (self::checkAccess($menu['route'])) {
                $checkMenus[] = $menu;
            }
        }

        return $checkMenus;
    }

    protected function checkAccess($route)
    {
        $accessControl = Yii::$app->behaviors['access'];
        foreach ($accessControl->allowActions as $allowAction) {
            if (substr($allowAction, -1) === '*') {
                $allowAction = rtrim($allowAction, '*');
                if ($allowAction === '' || strpos($route, AdminHelper::normalizeRoute($allowAction, true)) === 0) {
                    return true;
                }
            } else {
                if ($route === $allowAction) {
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * 获取菜单树形数组
     * @param string $menuType 菜单类型名称
     * @param int|null $id ID
     * @param int $maxLevel 最大等级
     * @return array 返回菜单树形数组
     */
    public static function getAssignedMenu($menuType, $id = null, $maxLevel = 0)
    {
        $treeMenus = static::getTree(static::getMenus($menuType), $id, $maxLevel);
        return static::navItems($treeMenus);
    }

    /**
     * 获取树形数组
     * @param array $array 数组
     * @param int|null $id ID
     * @param int $maxLevel 最大等级
     * @return array 返回树形数组
     */
    public static function getTree($array, $id = null, $maxLevel = 0)
    {
        $tree = [];
        foreach ($array as $item) {
            if ($item['parent'] == $id && ($item['level'] < $maxLevel || $maxLevel == 0)) {
                $item['children'] = static::getTree($array, $item['id'], $maxLevel);
                $tree[] = $item;
            }
        }
        return $tree;
    }

    /**
     * 返回线性二维数组菜单
     * @param string $menuType 菜单类型
     * @param array $menus
     * @param int $id ID
     * @return array
     */
    public static function getLineMenus($menuType = '', $menus = [], $id = 0)
    {
        $dataset = static::buildData($menuType, $menus);
        $newData = [];
        foreach ($dataset as $pid => $item) {
            $end = end($item);
            $res = array_merge($end, ['end' => 1]);
            $item[$res['id']] = $res;
            $newData[$pid] = $item;
        }
        return static::makeTree($id, $newData);
    }

    /**
     * 将菜单数组转换为 bootstrap Nav 所需的数组
     * @param array $menus
     * @return array
     */
    public static function navItems($menus)
    {
        $items = [];
        foreach ($menus as $menu) {
            $data = json_decode($menu['data'], true);
            $isCat = isset($data['lang-cat']) && !empty(isset($data['lang-cat']));
            $lable = $isCat ? Yii::t($data['lang-cat'], $menu['name']) : $menu['name'];
            if (empty($menu['children'])) {
                if ($menu['type'] === 'separator') {
                    $items[$menu['id']] = Html::tag('li', '', ['class' => 'divider']);
                } else {
                    $items[$menu['id']] = [
                        'label' => $lable,
                        'url' => empty($menu['route']) ? null : [strstr($menu['route'], '/')]
                    ];
                }
            } else {
                $items[$menu['id']] = [
                    'label' => $lable,
                    'url' => null,
                    'items' => static::navItems($menu['children']),
                    'linkOptions' => [
                        'role' => 'button',
                        'aria-haspopup' => 'true'
                    ]
                ];
            }
        }
        return $items;
    }

    /**
     * 得到当前位置菜单数组
     * @param int $myid
     * @param string $menuType
     * @param array $newarr
     * @return boolean|array
     */
    public static function getPos($myid, $menuType, &$newarr = [])
    {
        $newData = [];
        foreach (static::getMenus($menuType) as $item) {
            $newData[$item['id']] = $item;
        }

        if (!isset($newData[$myid])) {
            return false;
        }

        $newarr[] = $newData[$myid];
        $pid = $newData[$myid]['parent'];
        if (isset($newData[$pid])) {
            self::getPos($pid, $menuType, $newarr);
        }
        $a = [];
        if (is_array($newarr)) {
            krsort($newarr);
            foreach ($newarr as $v) {
                $a[$v['id']] = $v;
            }
        }
        return $a;
    }

    /**
     * 获取菜单的顶级项
     * @param int $myid
     * @param string $menuType
     * @param array $array
     * @return array
     */
    public static function getTop($myid, $menuType = '', $array = [])
    {
        $menu = [];
        if (empty($array)) {
            $array = static::getMenus($menuType) ?? [];
        }
        foreach ($array as $item) {
            if ($item['id'] == $myid) {
                if ($item['parent'] === null) {
                    $menu = $item;
                } else {
                    $menu = static::getTop($item['parent'], $menuType, $array);
                }
            }
        }
        return $menu;
    }

    /**
     * 添加侧栏菜单项
     * @param string $active
     * @return array
     */
    public static function addSubmenus($active = null)
    {
        $items = [];
        foreach (static::$entries as $key => $item) {
            if (isset($item['url'])) {
                $items[] = [
                    'label' => $item['label'],
                    'url' => $item['url'],
                    'active' => $active == $key
                ];
            } else {
                $items[] = '<li class="nav-header">' . $item['label'] . '</li>';
            }
        }
        return $items;
    }

    /**
     * 添加一项侧栏菜单
     * @param string $label
     * @param string|array $url
     * @param string|null $key
     */
    public static function addEntry($label, $url = '', $key = null)
    {
        if ($key === null) {
            if ($url === '') {
                static::$entries[] = ['label' => $label];
            } else {
                static::$entries[] = ['label' => $label, 'url' => $url];
            }
        } else {
            static::$entries[$key]['label'] = $label;
            if ($url !== '') {
                static::$entries[$key]['url'] = $url;
            }
        }
    }

    /**
     * 添加多项侧栏菜单
     * @param array $entries
     */
    public static function addEntries($entries = [])
    {
        if (count($entries)) {
            foreach ($entries as $entry) {
                static::addEntry($entry['label'], $entry['url'] ?? '', $entry['key'] ?? null);
            }
        }
    }

    /**
     * 获取侧栏菜单项
     * @param string $key
     * @return array
     */
    public static function getEntries($key = null)
    {
        return $key === null ? static::$entries : static::$entries[$key];
    }

    /**
     * 移除侧栏菜单
     * @param string $key
     */
    public static function unEntries($key = null)
    {
        if ($key === null) {
            static::$entries = [];
        } else {
            unset(static::$entries[$key]);
        }
    }

    /**
     * 获取 $myid 下的子数组
     * @param int $myid ID
     * @return array|boolean
     */
    public static function getChild($myid)
    {
        $newData = [];
        foreach (static::getMenus() as $item) {
            $newData[$item['id']] = $item;
        }
        $newArr = [];
        if (is_array($newData)) {
            foreach ($newData as $id => $a) {
                if ($a['parent'] == $myid) {
                    $newArr[$id] = $a;
                }
            }
        }

        return $newArr ?? false;
    }

    /**
     * 获取 $myid 下的所有子 id 数组
     * @param int $myid ID
     * @return array
     */
    public static function getAllChildids($myid = null)
    {
        $child = static::getChild($myid);
        $total = $child ? count($child) : 0;
        $ids = [];
        if ($total > 0) {
            foreach ($child as $id => $value) {
                $ids[] = $id;
                $ids = array_merge($ids, static::getAllChildids($id));
            }
        }
        return $ids;
    }

    /**
     * 给每个菜单项计算并增加 lft 和 rgt 值
     * @param array $menus
     * @return array
     */
    public static function resetLftRgt($menus = [])
    {
        $lineMenus = static::getLineMenus('', $menus);

        foreach ($lineMenus as $key => &$item) {
            $childrenCount = count(static::getAllChildids($item['id'])) + 1;
            if ($item['parent'] === null) {
                $item['lft'] = $key * 2;
                $item['rgt'] = $key * 2 + $childrenCount * 2 - 1;
            } else {
                $preMenu = $lineMenus[$key - 1];
                $pml = $preMenu['lft'];
                $pmr = $preMenu['rgt'];
                $levelDiff = $preMenu['level'] - $item['level'];
                $item['lft'] = $pmr - $pml > 1 ? $pml + 1 : $pmr + 1 + $levelDiff;
                $item['rgt'] = $childrenCount == 1 ? $item['lft'] + 1 : $item['lft'] + $childrenCount * 2 - 1;
            }
        }

        return $lineMenus;
    }

    /**
     * 格式化数据
     * @param string $menuType 菜单类型
     * @param array $data 菜单数据
     * @return array
     */
    private static function buildData($menuType = '', $data = [])
    {
        if (empty($data)) {
            $data = static::getMenus($menuType) ?? [];
        }
        $menus = [];
        foreach ($data as $item) {
            $parent = $item['parent'] ?? 0;
            $menus[$parent][$item['id']] = $item;
        }
        return $menus;
    }

    /**
     * 生成树核心
     * @param int $myid
     * @param array $data
     * @param array $res
     * @return array
     */
    private static function makeTree($myid, $data = [], &$res = [])
    {
        if (!isset($data[$myid])) {
            return [];
        }

        foreach ($data[$myid] as $id => $item) {
            $res[] = $item;
            if (isset($data[$id])) {
                static::makeTree($id, $data, $res);
            }
        }

        return $res;
    }

    /**
     * 获取当前应用目录名称
     * @return bool|string 成功返回应用目录名称，否则返回 false
     */
    public static function getClient()
    {
        $rootPath = Yii::getAlias('@root/');
        $basePath = Yii::$app->basePath;
        if (StringHelper::startsWith($basePath, $rootPath)) {
            return substr($basePath, strlen($rootPath));
        } else {
            return false;
        }
    }

    /**
     * 获取相对项目路径
     * @param string $path 绝对路径
     * @return string
     */
    public static function getRelativePath($path)
    {
        $root = Yii::getAlias('@root');
        if (StringHelper::startsWith($path, $root)) {
            $path = '.' . substr($path, strlen($root));
        }
        return $path;
    }

    /**
     * 模型获取的错误数组信息转化成显示的字符串
     * @param array $errors
     * @param string $glue
     * @param bool $isKey
     * @return string
     */
    public static function errorsToString($errors, $glue = '<br>', $isKey = false)
    {
        $errorsString = '';
        foreach ($errors as $key => $error) {
            foreach ($error as $item) {
                $errorsString .= $glue . ($isKey ? $key . ': ' : '') . $item;
            }
        }
        return substr($errorsString, strlen($glue));
    }

    public static function addAssetJsFile($view, $sourcePath, $js, $depends = [], $isAfter = true)
    {
        $asset = new AppAsset();
        $asset->sourcePath = $sourcePath;
        $asset->setDepends($depends);
        AppAsset::register($view);
        $asset->addJs($js, $isAfter);
    }

    public static function addAssetCssFile($view, $sourcePath, $css, $depends = [], $isAfter = true)
    {
        $asset = new AppAsset();
        $asset->sourcePath = $sourcePath;
        $asset->setDepends($depends);
        AppAsset::register($view);
        $asset->addCss($css, $isAfter);
    }

    /**
     * 显示小部件
     * @param $name
     * @param array $config
     * @return string
     */
    public static function widget($name, $config = [])
    {
        $className = self::getClient() . '\widgets\\' . $name . '\Widget';
        $theme = Yii::$app->params['global']['theme'];
        $themeClassName = self::getClient() . '\themes\\' . $theme . '\widgets\\' . $name . '\Widget';
        if (class_exists($className)) {
            return $className::widget($config);
        } elseif (class_exists($themeClassName)) {
            return $themeClassName::widget($config);
        } else {
            return '';
        }
    }

    /**
     * 显示在 $position 位置挂钩的所有小部件
     * @param string $position 位置标识
     * @param array $attrs 属性
     * @return mixed
     */
    public static function hook($position, $attrs = [])
    {
        $client = \backend\models\Clients::findOne(['name' => static::getClient()]);
        $widgets = \backend\modules\widgets\models\Widgets::find()
            ->where([
                'position' => $position,
                'client_id' => $client->id,
                'published' => 1
            ])
            ->orderBy('ordering')
            ->all();
        if ($widgets) {
            $wHtml = '';
            foreach ($widgets as $widget) {
                $wHtml .= self::widget($widget->widget, json_decode($widget->params, true));
            }
            return $wHtml;
        } else {
            return '';
        }
    }

    /**
     * 运行其它应用
     * @param $client 应用名称
     * @return mixed|\yii\console\Application|\yii\web\Application
     */
    public static function runApp($client)
    {
        if (self::getClient() === $client) {
            return Yii::$app;
        } else {
            return Yii::$app->cache->getOrSet($client . '_client_app', function () use ($client) {
                $advanced = Configs::instance()->advanced;
                $config = [];
                foreach ($advanced[$client] as $configPath) {
                    $config = ArrayHelper::merge($config, require(Yii::getAlias($configPath)));
                }
                unset($config['bootstrap'], $config['on beforeRequest']);
                $app = new \yii\web\Application($config);
                return $app;
            });
        }
    }

    public static function menu($type,$options=[])
    {
        $menus = Menu::find()->where(['menutype'=>$type,'published'=>1])->orderBy('lft')->all();
        return $menus;
    }
}
